#pragma once

#include <iostream>
#include <memory>
#include <mutex>
#include <mysql/mysql.h>
#include "erro.hpp"
#include <jsoncpp/json/json.h>
#include "util.hpp"

static const char *host = "localhost";
static const char *user = "connector";
static const char *password = "Xianghao2020!";
static const char *db = "con";
static int port = 3306;

class Sql
{
public:
    Sql() : _sq(nullptr)
    {
    }
    ~Sql()
    {
        mysql_close(_sq);
    }

public:
    static Sql *getinstance()
    {   
        if(_instance == nullptr)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if (_instance == nullptr)
            {
                _instance = new Sql;
                return _instance;
            }
            return _instance;
           
        }
        return _instance;

    }
    void init()
    {
        _sq = mysql_init(nullptr);
        if (_sq == nullptr)
        {
            logmessage(FATAL, "数据库初始化失败");
            exit(MYSQLINITERRO);
        }
        mysql_set_character_set(_sq, "utf8");
        if (mysql_real_connect(_sq, host, user, password, db, port, nullptr, 0) == nullptr)
        {
            logmessage(FATAL, "连接到数据库失败");
            exit(MYSQLCONNERRO);
        }
        // _stmt = mysql_stmt_init(_sq);
        // if(_stmt == nullptr){
        //     logmessage(FATAL,"STMT 初始化失败");
        //     exit(MYSQLSTMTERRO);
        // }
        logmessage(NORMAL, "数据库初始化且连接成功");
    }
    bool createuser(const char *username, const char *password, const char *email, const char *nickname, std::string &message)
    {
        MYSQL_STMT *stmt = mysql_stmt_init(_sq);
        // 使用预编译防sql注入
        if (mysql_stmt_prepare(stmt, "insert into userlist(username,password,email,nickname) values (?,?,?,?)", sizeof("insert into userlist(username,password,email,nickname) values (?,?,?,?)")))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "预编译失败");
            mysql_stmt_close(stmt);
            return false;
        }

        MYSQL_BIND bind[4];
        memset(bind, 0, sizeof(bind));
        bind[0].buffer_type = MYSQL_TYPE_STRING;
        bind[0].buffer = const_cast<char *>(username);
        bind[0].buffer_length = strlen(username);

        bind[1].buffer_type = MYSQL_TYPE_STRING;
        bind[1].buffer = const_cast<char *>(password);
        bind[1].buffer_length = strlen(password);

        bind[2].buffer_type = MYSQL_TYPE_STRING;
        bind[2].buffer = const_cast<char *>(email);
        bind[2].buffer_length = strlen(email);

        bind[3].buffer_type = MYSQL_TYPE_STRING;
        bind[3].buffer = const_cast<char *>(nickname);
        bind[3].buffer_length = strlen(nickname);

        if (mysql_stmt_bind_param(stmt, bind))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt bind param erro");
            mysql_stmt_close(stmt);
            return false;
        }

        if (mysql_stmt_execute(stmt))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt execute faild:%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }
        logmessage(NORMAL, "用户注册成功");
        mysql_stmt_close(stmt);
        return true;
    }
    bool login(std::string username, std::string password, std::string &message, Userinfos &userinfos)
    {
        MYSQL_STMT *stmt = mysql_stmt_init(_sq);
        const char *sql = "select id, nickname, score, total_cont, win_cont from userlist where username=? and password=?";
        if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "预编译失败,%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }
        MYSQL_BIND bind[2];
        memset(bind, 0, sizeof(bind));
        bind[0].buffer_type = MYSQL_TYPE_STRING;
        bind[0].buffer = const_cast<char *>(username.c_str());
        bind[0].buffer_length = username.size();

        bind[1].buffer_type = MYSQL_TYPE_STRING;
        bind[1].buffer = const_cast<char *>(password.c_str());
        bind[1].buffer_length = password.size();

        if (mysql_stmt_bind_param(stmt, bind))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt bind param erro");
            mysql_stmt_close(stmt);
            return false;
        }
        if (mysql_stmt_execute(stmt))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt execute faild:%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }

        if (mysql_stmt_store_result(stmt) != 0)
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt execute faild:%s", message.c_str());
            mysql_stmt_close(stmt);
            return false;
        }

        int rownum = mysql_stmt_num_rows(stmt);
        if (rownum != 1)
        {
            // 登录失败
            message = "账号或密码错误";
            logmessage(NORMAL, "登录时账号或密码错误");
            mysql_stmt_close(stmt);
            return false;
        }
        // 关键修复3：绑定结果集缓冲区（预处理语句必须显式绑定结果）
        MYSQL_BIND result_bind[5];
        memset(result_bind, 0, sizeof(result_bind));
        // 绑定 id（整数）
        int id;
        result_bind[0].buffer_type = MYSQL_TYPE_LONG;
        result_bind[0].buffer = &id;
        // 绑定 nickname（字符串）
        char nickname[128] = {0};
        result_bind[1].buffer_type = MYSQL_TYPE_STRING;
        result_bind[1].buffer = nickname;
        result_bind[1].buffer_length = sizeof(nickname);
        // 绑定 score（整数）
        int score;
        result_bind[2].buffer_type = MYSQL_TYPE_LONG;
        result_bind[2].buffer = &score;
        // 绑定 total_cont（整数）
        int total_cont;
        result_bind[3].buffer_type = MYSQL_TYPE_LONG;
        result_bind[3].buffer = &total_cont;
        // 绑定 win_cont（整数）
        int win_cont;
        result_bind[4].buffer_type = MYSQL_TYPE_LONG;
        result_bind[4].buffer = &win_cont;

        if (mysql_stmt_bind_result(stmt, result_bind) != 0)
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "结果绑定失败: %s", message.c_str());
            mysql_stmt_close(stmt);
            return false;
        }

        // 关键修复4：用 mysql_stmt_fetch 获取结果（预处理语句专用）
        if (mysql_stmt_fetch(stmt) != 0)
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "获取结果失败: %s", message.c_str());
            mysql_stmt_close(stmt);
            return false;
        }

        // 赋值给 userinfos
        userinfos.id = id;
        userinfos.nickname = nickname;
        userinfos.score = score;
        userinfos.total_cont = total_cont;
        userinfos.win_cont = win_cont;

        mysql_stmt_close(stmt);
        logmessage(NORMAL,"登录成功");
        return true;
    }

    bool getuserinfos(uint64_t uid,Json::Value& user_info,std::string& message){
        MYSQL_STMT *stmt = mysql_stmt_init(_sq);
        const char *sql = "select nickname, score, total_cont, win_cont from userlist where id = ?";
        if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "预编译失败,%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }
        MYSQL_BIND bind[1];
        memset(bind, 0, sizeof(bind));
        bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
        bind[0].buffer = &uid;
        bind[0].buffer_length = sizeof(uid);
        if (mysql_stmt_bind_param(stmt, bind))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt bind param erro");
            mysql_stmt_close(stmt);
            return false;
        }
        if (mysql_stmt_execute(stmt))
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt execute faild:%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }

        if (mysql_stmt_store_result(stmt) != 0)
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "stmt execute faild:%s", message.c_str());
            mysql_stmt_close(stmt);
            return false;
        }

        int rownum = mysql_stmt_num_rows(stmt);
        if (rownum != 1)
        {
            // 登录失败
            message = "用户不存在";
            logmessage(NORMAL, "利用uid查询用户数据时出错");
            mysql_stmt_close(stmt);
            return false;
        }
        // 关键修复3：绑定结果集缓冲区（预处理语句必须显式绑定结果）
        MYSQL_BIND result_bind[4];
        memset(result_bind, 0, sizeof(result_bind));
        // 绑定 nickname（字符串）
        char nickname[128] = {0};
        result_bind[0].buffer_type = MYSQL_TYPE_STRING;
        result_bind[0].buffer = nickname;
        result_bind[0].buffer_length = sizeof(nickname);
        // 绑定 score（整数）
        int score;
        result_bind[1].buffer_type = MYSQL_TYPE_LONG;
        result_bind[1].buffer = &score;
        // 绑定 total_cont（整数）
        int total_cont;
        result_bind[2].buffer_type = MYSQL_TYPE_LONG;
        result_bind[2].buffer = &total_cont;
        // 绑定 win_cont（整数）
        int win_cont;
        result_bind[3].buffer_type = MYSQL_TYPE_LONG;
        result_bind[3].buffer = &win_cont;

        if (mysql_stmt_bind_result(stmt, result_bind) != 0)
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "结果绑定失败: %s", message.c_str());
            mysql_stmt_close(stmt);
            return false;
        }

        // 关键修复4：用 mysql_stmt_fetch 获取结果（预处理语句专用）
        if (mysql_stmt_fetch(stmt) != 0)
        {
            message = mysql_stmt_error(stmt);
            logmessage(ERROR, "获取结果失败: %s", message.c_str());
            mysql_stmt_close(stmt);
            return false;
        }
        user_info["username"] = nickname;
        user_info["score"] = score;
        user_info["total_count"] = total_cont;
        user_info["win_count"] = win_cont;
        mysql_stmt_close(stmt);
        logmessage(NORMAL,"获取信息成功");
        return true;
        
    }

    bool win(uint64_t uid){
        MYSQL_STMT *stmt = mysql_stmt_init(_sq);
        const char *sql = "update userlist set score=score+15,total_cont=total_cont+1,win_cont=win_cont+1 where id=?";
        if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
        {
            
            logmessage(ERROR, "预编译失败,%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }
        MYSQL_BIND bind[1];
        memset(bind, 0, sizeof(bind));
        bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
        bind[0].buffer = &uid;
        bind[0].buffer_length = sizeof(uid);
        if (mysql_stmt_bind_param(stmt, bind))
        {
           
            logmessage(ERROR, "stmt bind param erro");
            mysql_stmt_close(stmt);
            return false;
        }
        if (mysql_stmt_execute(stmt))
        {
           
            logmessage(ERROR, "stmt execute faild:%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }
        return true ;
    }

    bool loser(uint64_t uid){
        MYSQL_STMT *stmt = mysql_stmt_init(_sq);
        const char *sql = "update userlist set score=score-15,total_cont=total_cont+1 where id=?";
        if (mysql_stmt_prepare(stmt, sql, strlen(sql)))
        {
           
            logmessage(ERROR, "预编译失败,%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }
        MYSQL_BIND bind[1];
        memset(bind, 0, sizeof(bind));
        bind[0].buffer_type = MYSQL_TYPE_LONGLONG;
        bind[0].buffer = &uid;
        bind[0].buffer_length = sizeof(uid);
        if (mysql_stmt_bind_param(stmt, bind))
        {
            
            logmessage(ERROR, "stmt bind param erro");
            mysql_stmt_close(stmt);
            return false;
        }
        if (mysql_stmt_execute(stmt))
        {
           
            logmessage(ERROR, "stmt execute faild:%s", mysql_stmt_error(stmt));
            mysql_stmt_close(stmt);
            return false;
        }
        return true ;
    }
    



private:
    static Sql *_instance;
    MYSQL *_sq;

    static std::mutex _mutex; // 互斥锁，保障访问安全;
};
Sql *Sql::_instance = nullptr;
std::mutex Sql::_mutex;
